package cn.cas.mango.service;

import cn.cas.mango.dto.HotChartValue;
import cn.cas.mango.dto.Message;
import cn.cas.mango.dto.News;
import cn.cas.mango.dto.WebSiteTypeInfo;
import cn.cas.mango.dto.param.BaseParam;
import cn.cas.mango.dto.param.DetailParam;
import cn.cas.mango.dto.param.DetailSimParam;
import cn.cas.mango.dto.param.QueryParam;
import cn.cas.mango.util.EsTypeTransUtil;
import cn.cas.mango.util.Pair;
import cn.cas.mango.util.RenderDateUtil;
import cn.cas.mango.util.RequestUtil.BuildRequest;
import cn.cas.mango.util.ResponseUtil.ResponseUtil;
import cn.cas.mango.util.TimeUtil;
import cn.cas.mango.util.paramCheck.DetailSimParamCheckUtil;
import cn.cas.mango.util.paramCheck.QueryParamCheckUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.CalendarInterval;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.util.NamedValue;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 基于关键词、实体、搜索获取相似新闻
 */
@Service
@Slf4j
public class GetReCmdByService2 extends BaseService {
  @Autowired
  private ElasticsearchClient elasticsearchClient;
  @Autowired
  private ConfigService config;
  @Autowired
  private DetailService detailService;
  @Value("${elasticsearch.newsDetail.pageSize}")
  private Integer detailPageSize;
  @Autowired
  private BuildRequest buildRequest;
  @Autowired
  private CountService countService;
  private int monthShow = 2;

  @Override
  public Message run(BaseParam baseParam) {
    System.out.println(config.getFuzzyValue());
    DetailSimParam detailSimParam = (DetailSimParam) baseParam;
    Map<String, Object> errMessage = DetailSimParamCheckUtil.checkParam(detailSimParam);
    String isSuccess = (String) errMessage.get("isSuccess");
    if (!isSuccess.equals("ok")) {
      return Message.unSuccess(isSuccess);
    }
    TimeUtil timeUtil = new TimeUtil();
    double alertValue = detailSimParam.getAlert();
    QueryParam queryParam = wrapQueryParam(baseParam, detailPageSize);


    final List<String> axisDay = RenderDateUtil.getDistanceDate(queryParam.getStartDate(),
        queryParam.getEndDate());

    log.info("detail_first_query: {}", queryParam);
    Map<String, String> webSiteTypeNameTotal = config.getWebSiteTypeName();
    Map<String, String> webSiteTypeName = new HashMap<>();
    if (queryParam.getWebSiteTypeArray() == null || queryParam.getWebSiteTypeArray().isEmpty()) {
      webSiteTypeName = new HashMap<>(webSiteTypeNameTotal);
    } else {
      Set<String> collect = queryParam.getWebSiteTypeArray().stream().map(x -> ((String) x._get())).collect(Collectors.toSet());

      for (String s: collect) {
        webSiteTypeName.put(s, webSiteTypeNameTotal.get(s));
      }
    }
    SearchRequest.Builder requestBuilder;
    if (detailSimParam.getRecommendType().equals("mlt")) {

      requestBuilder = buildRequest.buildMltRequest(queryParam);
    } else {
      requestBuilder =  buildRequest.buildRequest(queryParam);
    }
    requestBuilder = requestBuilder.sort((SortOptions) queryParam.get3Sorts().get(2));
    final List<String> getDistanceMonth = RenderDateUtil.getDistanceMonth(axisDay);
    final List<String> getDistanceYear = RenderDateUtil.getDistanceYear(axisDay);
    final LinkedHashMap<String, List<String>> getDistanceYearMap = RenderDateUtil.getDistanceYearMap(
        axisDay);

    NamedValue<SortOrder> order = new NamedValue<>("_key", SortOrder.Asc);
    String field = "news_publicdate";
    requestBuilder
        .aggregations("type-day",
            aggBuilder ->
                aggBuilder.dateHistogram(
                        gram -> gram.field(field).calendarInterval(CalendarInterval.Day)
                            .format("yyyy-MM-dd")
                            .minDocCount(1).order(order))
                    .aggregations("group_by_type",
                        a -> a.terms(t -> t.field("news_website_type").size(axisDay.size())))
        )
        .aggregations("count-day",
            aggBuilder -> aggBuilder.dateHistogram(
                gram -> gram.field(field).calendarInterval(CalendarInterval.Day)
                    .format("yyyy-MM-dd")
                    .minDocCount(1).order(order))
        )

    ;
    if (getDistanceMonth.size() > monthShow) {
      requestBuilder
          .aggregations("count-month",
              aggBuilder -> aggBuilder.dateHistogram(
                  gram -> gram.field(field).calendarInterval(CalendarInterval.Month)
                      .format("yyyy-MM")
                      .minDocCount(1).order(order))
          )

      ;
    }
    if (axisDay.size() > 365 * 3) {
      requestBuilder
          .aggregations("count-year",
              aggBuilder -> aggBuilder.dateHistogram(
                  gram -> gram.field(field).calendarInterval(CalendarInterval.Year)
                      .format("yyyy")
                      .minDocCount(1).order(order)))

      ;
    }
    SearchRequest searchRequest = requestBuilder.build();
    String queryJsonStr = searchRequest.toString().split("typed_keys=true")[1];
    log.info("queryJsonStr: {}", queryJsonStr);
    JSONObject queryJson = JSONObject.parseObject(queryJsonStr);
    log.info("Search Request Json:{}", queryJson);
    RestTemplateBuilder builder = new RestTemplateBuilder();
    RestTemplate restTemplate = builder.basicAuthentication(config.getUsername(), config.getPassword()).build();
    log.info("Start Request:{}s", timeUtil.getCost());
    String url = "http://" + config.getHost() + ":" + config.getPort() + "/" + buildRequest.partitionIndex(config.getIndex(), queryParam.getStartDate(),
        queryParam.getEndDate()) + "/_search";
    ResponseEntity<JSONObject> responseEntity = restTemplate.postForEntity(url, queryJson,
        JSONObject.class);
    log.info("Get Response:{}s", timeUtil.getCost());
    JSONObject responseJson = Objects.requireNonNull(responseEntity.getBody())
        .getJSONObject("aggregations");
    final Map<Integer, String> colorMap = config.getColorMap();
    final LinkedHashMap<String, List<String>> monthMap = RenderDateUtil.getDistanceMonthMap(
        axisDay);
    //处理按天统计数据
    JSONArray countDayArray = responseJson.getJSONObject("count-day").getJSONArray("buckets");
    Map<String, Integer> dayCountMap = new HashMap<>();
    LinkedHashMap<String, Integer> dayTotalMap = this.countService.getDayCountMap(
        queryParam.getStartDate(), queryParam.getEndDate());
    int dayMinCount;
    int dayMaxCount;
    for (Object row : countDayArray) {
      JSONObject rowJson = (JSONObject) row;
      String day = rowJson.getString("key_as_string");
      Integer count = rowJson.getInteger("doc_count");
      dayCountMap.put(day, count);
    }
    List<HotChartValue> countDayList = new ArrayList<>();
    List<HotChartValue> hotDayList = new ArrayList<>();
    List<Integer> dayCount = new ArrayList<>();
    List<Float> dayHot = new ArrayList<>();
    for (String day : axisDay) {
      HotChartValue countValue = new HotChartValue();
      HotChartValue hotValue = new HotChartValue();
      countValue.setDate(day);
      hotValue.setDate(day);
      if (dayCountMap.get(day) != null) {
        countValue.setValue(dayCountMap.get(day));
        dayCount.add(dayCountMap.get(day));
        float hotCount = (float) dayCountMap.get(day)
            / dayTotalMap.get(day);
        hotValue.setValue(hotCount * 100);
        dayHot.add(hotCount * 100);
      } else {
        countValue.setValue(0);
        dayCount.add(0);
        hotValue.setValue(0.0);
        dayHot.add(0.0F);
      }
      countDayList.add(countValue);
      hotDayList.add(hotValue);
    }
    Integer[] countArray = new Integer[dayCount.size()];
    countArray = dayCount.toArray(countArray);
    Arrays.sort(countArray);
    dayMaxCount = countArray[countArray.length - 1];
    dayMinCount = countArray[0];


    for (HotChartValue value : countDayList) {
      int value1 = (int)value.getValue();
      int a = getColorCast(countArray, value1);
      value.setColor(colorMap.get(a));
    }
    Float[] hotArray = new Float[dayHot.size()];
    hotArray = dayHot.toArray(hotArray);
    Arrays.sort(hotArray);

    for (HotChartValue value : hotDayList) {
      float value1 = Float.parseFloat(String.valueOf(value.getValue()));
      int a = getColorCast(hotArray, value1);
      value.setColor(colorMap.get(a));

    }
    log.info("数量按天处理End:{}s", timeUtil.getCost());
    //处理按月统计数据
    List<HotChartValue> monthCountList = new ArrayList<>();
    int monthMinCount = 0;
    int monthMaxCount = 0;
    List<HotChartValue> monthHotList = new ArrayList<>();
    Float[] monthHotArray = new Float[0];
    if (getDistanceMonth.size() > monthShow) {
      JSONArray countMonthArray = responseJson.getJSONObject("count-month").getJSONArray("buckets");
      Map<String, Integer> monthCountMap = new HashMap<>();
      for (Object row : countMonthArray) {
        JSONObject rowJson = (JSONObject) row;
        String day = rowJson.getString("key_as_string");
        Integer count = rowJson.getInteger("doc_count");
        monthCountMap.put(day, count);
        if (count > monthMaxCount) {
          monthMaxCount = count;
        }
        if (monthMinCount == 0) {
          monthMinCount = count;
        }
        if (count < monthMinCount) {
          monthMinCount = count;
        }
      }

      List<Integer> monthCount = new ArrayList<>();
      for (String day : getDistanceMonth) {
        HotChartValue countValue = new HotChartValue();
        countValue.setDate(day);
        if (monthCountMap.get(day) != null) {
          countValue.setValue(monthCountMap.get(day));
          monthCount.add(monthCountMap.get(day));
        } else {
          countValue.setValue(0);
          monthCount.add(0);
        }
        monthCountList.add(countValue);
      }

      Integer[] monthArray = new Integer[monthCount.size()];
      monthArray = monthCount.toArray(monthArray);
      Arrays.sort(monthArray);

      for (HotChartValue value : monthCountList) {
        int value1 = (int) value.getValue();
        int a = getColorCast(monthArray, value1);
        value.setColor(colorMap.get(a));
      }

      //处理按月统计热度数据（将天热度数据加和）
      List<Float> monthHot = new ArrayList<>();
      for (String month : monthMap.keySet()) {
        float hotValue = 0.0f;
        for (String day : monthMap.get(month)) {
          for (HotChartValue value : hotDayList) {
            if (value.getDate().equals(day)) {
              hotValue += Float.parseFloat(String.valueOf(value.getValue()));
            }
          }
        }
        HotChartValue hotChartValue = new HotChartValue();
        hotChartValue.setDate(month);
        hotChartValue.setValue(hotValue);
        monthHotList.add(hotChartValue);
        monthHot.add(hotValue);
      }

      monthHotArray = new Float[monthHot.size()];
      monthHotArray = monthHot.toArray(monthHotArray);
      Arrays.sort(monthHotArray);

      for (HotChartValue value : monthHotList) {
        float value1 = Float.parseFloat(String.valueOf(value.getValue()));
        int a = getColorCast(monthHotArray, value1);
        value.setColor(colorMap.get(a));
      }
    }
    log.info("数量按月处理End:{}s", timeUtil.getCost());
    // 处理按年统计数据
    List<HotChartValue> yearCountList = new ArrayList<>();
    List<HotChartValue> yearHotList = new ArrayList<>();
    int yearMinCount = 0;
    int yearMaxCount = 0;
    Float[] yearHotArray = new Float[0];
    if (axisDay.size() > 365 * 3) {
      JSONArray countYearArray = responseJson.getJSONObject("count-year").getJSONArray("buckets");
      Map<String, Integer> yearCountMap = new HashMap<>();
      for (Object row : countYearArray) {
        JSONObject rowJson = (JSONObject) row;
        String day = rowJson.getString("key_as_string");
        Integer count = rowJson.getInteger("doc_count");
        yearCountMap.put(day, count);
        if (count > yearMaxCount) {
          yearMaxCount = count;
        }
        if (yearMinCount == 0) {
          yearMinCount = count;
        }
        if (count < yearMinCount) {
          yearMinCount = count;
        }
      }
      yearCountList = new ArrayList<>();
      List<Integer> yearCount = new ArrayList<>();
      for (String day : getDistanceYear) {
        HotChartValue countValue = new HotChartValue();
        HotChartValue hotValue = new HotChartValue();
        countValue.setDate(day);
        hotValue.setDate(day);
        if (yearCountMap.get(day) != null) {
          countValue.setValue(yearCountMap.get(day));
          yearCount.add(yearCountMap.get(day));
        } else {
          countValue.setValue(0);
          yearCount.add(0);
        }
        yearCountList.add(countValue);
      }

      Integer[] yearArray = new Integer[yearCount.size()];
      yearArray = yearCount.toArray(yearArray);
      Arrays.sort(yearArray);

      for (HotChartValue value : yearCountList) {
        int value1 = (int)value.getValue();
        int a = getColorCast(yearArray, value1);
        value.setColor(colorMap.get(a));
      }

      //处理按年统计热度数据（将天热度数据加和）
      List<Float> yearHot = new ArrayList<>();
      for (String year : getDistanceYearMap.keySet()) {
        float hotValue = 0.0f;
        for (String day : getDistanceYearMap.get(year)) {
          for (HotChartValue value : hotDayList) {
            if (value.getDate().equals(day)) {
              hotValue += Float.parseFloat(String.valueOf(value.getValue()));
            }
          }
        }
        HotChartValue hotChartValue = new HotChartValue();
        hotChartValue.setDate(year);
        hotChartValue.setValue(hotValue);
        yearHotList.add(hotChartValue);
        yearHot.add(hotValue);
      }

      yearHotArray = new Float[yearHot.size()];
      yearHotArray = yearHot.toArray(yearHotArray);
      Arrays.sort(yearHotArray);

      for (HotChartValue value : yearHotList) {
        float value1 = Float.parseFloat(String.valueOf(value.getValue()));
        int a = getColorCast(yearHotArray, value1);
        value.setColor(colorMap.get(a));
      }
    }
    log.info("数量按年处理End:{}s", timeUtil.getCost());
    // 按日处理类型
    JSONArray typeDayArray = responseJson.getJSONObject("type-day").getJSONArray("buckets");
    List<WebSiteTypeInfo> dayWebSiteTypeInfos = new ArrayList<>();
    for (Object object : typeDayArray) {
      JSONObject groupByType = ((JSONObject) object).getJSONObject("group_by_type");
      JSONArray innerBuckets = groupByType.getJSONArray("buckets");
      String innerDate = ((JSONObject) object).getString("key_as_string");
      for (Object bucket : innerBuckets) {
        WebSiteTypeInfo info = new WebSiteTypeInfo();
        info.setName(((JSONObject) bucket).getString("key"));
        info.setDate(innerDate);
        info.setCount(
            ((float) ((JSONObject) bucket).getLong("doc_count") / dayTotalMap.get(innerDate)
                * 100));
        dayWebSiteTypeInfos.add(info);
      }
    }
    Map<String, List<WebSiteTypeInfo>> typeDayMap = new HashMap<>();
    for (WebSiteTypeInfo info : dayWebSiteTypeInfos) {
      if (typeDayMap.get(info.getName()) != null) {
        typeDayMap.get(info.getName()).add(info);
      } else {
        List<WebSiteTypeInfo> list = new ArrayList<>();
        list.add(info);
        typeDayMap.put(info.getName(), list);
      }
    }
    String[] types = new String[]{"OVERSEAS", "WEBSITE", "HMT", "GOV", "ANTI"};
    for (String type : types) {
      boolean have = false;
      for (String name : typeDayMap.keySet()) {
        if (name.equals(type)) {
          have = true;
          break;
        }
      }
      if (!have) {
        typeDayMap.put(type, new ArrayList<>());
      }
    }
    int dayAlert = 0;
    List<JSONObject> seriesDay = new ArrayList<>();
    List<String> legendDay = new ArrayList<>();

    for (String type : typeDayMap.keySet()) {
      if (!webSiteTypeName.containsKey(type)) {
        continue;
      }
      legendDay.add(webSiteTypeName.get(type));
      JSONObject seriesObject = new JSONObject();
      seriesObject.put("news_website_type", type);
      seriesObject.put("name", webSiteTypeName.get(type));
      List<Double> countList = new ArrayList<>();
      List<WebSiteTypeInfo> infos = typeDayMap.get(type);
      for (String time : axisDay) {
        boolean fill = true;
        for (WebSiteTypeInfo info: infos) {
          if (time.equals(info.getDate())) {
            countList.add(info.getCount());
            dayAlert += info.getCount();
            fill = false;
          }
        }
        if (fill) {
          countList.add(0.0);
        }
      }
      seriesObject.put("data", countList);
      seriesDay.add(seriesObject);
    }

    log.info("按天处理类型End:{}s", timeUtil.getCost());
    // 按月处理类型
    List<JSONObject> seriesMonth = new ArrayList<>();
    List<String> legendMonth = new ArrayList<>();
    int monthAlert = 0;
    if (getDistanceMonth.size() > monthShow) {
      Map<String, List<WebSiteTypeInfo>> typeMonthMap = new HashMap<>();
      for (String type : typeDayMap.keySet()) {
        List<WebSiteTypeInfo> monthWebSiteTypeInfos = new ArrayList<>();
        for (String month : monthMap.keySet()) {
          WebSiteTypeInfo webSiteTypeInfo = new WebSiteTypeInfo();
          webSiteTypeInfo.setDate(month);
          webSiteTypeInfo.setName(type);
          float count = 0F;
          for (WebSiteTypeInfo dayInfo : typeDayMap.get(type)) {
            for (String day : monthMap.get(month)) {
              if (dayInfo.getDate().equals(day) && type.equals(dayInfo.getName())) {
                count += dayInfo.getCount();
              }
            }
            webSiteTypeInfo.setCount(count);
          }
          monthWebSiteTypeInfos.add(webSiteTypeInfo);
        }
        typeMonthMap.put(type, monthWebSiteTypeInfos);
      }

      for (String type : typeMonthMap.keySet()) {
        if (!webSiteTypeName.containsKey(type)) {
          continue;
        }
        legendMonth.add(webSiteTypeName.get(type));
        JSONObject seriesObject = new JSONObject();
        seriesObject.put("news_website_type", type);
        seriesObject.put("name", webSiteTypeName.get(type));
        List<Double> countList = new ArrayList<>();
        List<WebSiteTypeInfo> infos = typeMonthMap.get(type);
        for (String time : getDistanceMonth) {
          boolean fill = true;
          for (WebSiteTypeInfo info : infos) {
            if (time.equals(info.getDate())) {
              countList.add(info.getCount());
              monthAlert += info.getCount();
              fill = false;
            }
          }
          if (fill) {
            countList.add(0.0);
          }
        }
        seriesObject.put("data", countList);
        seriesMonth.add(seriesObject);
      }
    }
    log.info("按月处理类型End:{}s", timeUtil.getCost());

    // 按年处理类型
    List<JSONObject> seriesYear = new ArrayList<>();
    List<String> legendYear = new ArrayList<>();
    int yearAlert = 0;
    if (axisDay.size() > 365 * 3) {
      Map<String, List<WebSiteTypeInfo>> typeYearMap = new HashMap<>();
      for (String type : typeDayMap.keySet()) {
        List<WebSiteTypeInfo> yearWebSiteTypeInfos = new ArrayList<>();
        for (String year : getDistanceYearMap.keySet()) {
          WebSiteTypeInfo webSiteTypeInfo = new WebSiteTypeInfo();
          webSiteTypeInfo.setDate(year);
          webSiteTypeInfo.setName(type);
          float count = 0F;
          for (WebSiteTypeInfo dayInfo : typeDayMap.get(type)) {
            for (String day : getDistanceYearMap.get(year)) {
              if (dayInfo.getDate().equals(day) && type.equals(dayInfo.getName())) {
                count += dayInfo.getCount();
              }
            }
            webSiteTypeInfo.setCount(count);
          }
          yearWebSiteTypeInfos.add(webSiteTypeInfo);
        }
        typeYearMap.put(type, yearWebSiteTypeInfos);
      }

      for (String type : typeYearMap.keySet()) {
        if (!webSiteTypeName.containsKey(type)) {
          continue;
        }
        legendYear.add(webSiteTypeName.get(type));
        JSONObject seriesObject = new JSONObject();
        seriesObject.put("news_website_type", type);
        seriesObject.put("name", webSiteTypeName.get(type));
        List<Double> countList = new ArrayList<>();
        List<WebSiteTypeInfo> infos = typeYearMap.get(type);
        for (String time : getDistanceYear) {
          boolean fill = true;
          for (WebSiteTypeInfo info : infos) {
            if (time.equals(info.getDate())) {
              countList.add(info.getCount());
              yearAlert += info.getCount();
              fill = false;
            }
          }
          if (fill) {
            countList.add(0.0);
          }
        }
        seriesObject.put("data", countList);
        seriesYear.add(seriesObject);
      }
    }
    log.info("按年处理类型End:{}s", timeUtil.getCost());
    Map<String, Object> statisticDay = new HashMap<>();
    statisticDay.put("series", seriesDay);
    statisticDay.put("xAxis", axisDay);
    statisticDay.put("legend", legendDay);
    Map<String, Object> statisticMonth = new HashMap<>();
    statisticMonth.put("series", seriesMonth);
    statisticMonth.put("xAxis", getDistanceMonth);
    statisticMonth.put("legend", legendMonth);
    Object[] statistic;
    String[] tag;
    double[] alert;
    float[] dayArray = wrapHot(dayHot);
    float minDayHot = dayArray[0];
    float maxDayHot = dayArray[dayArray.length - 1];

    float minMonthHot = 0.0f, maxMonthHot = 0.0f;
    if (monthHotArray.length != 0) {
      minMonthHot = monthHotArray[0];
      maxMonthHot = monthHotArray[monthHotArray.length - 1];
    }

    float minYearHot = 0.0f, maxYearHot = 0.0f;
    if (yearHotArray.length != 0) {
      minYearHot = yearHotArray[0];
      maxYearHot = yearHotArray[yearHotArray.length - 1];
    }
    if (axisDay.size() > 365 * 3) {
      Map<String, Object> statisticYear = new HashMap<>();
      statisticYear.put("series", seriesYear);
      statisticYear.put("xAxis", getDistanceYear);
      statisticYear.put("legend", legendYear);
      statistic = new Map[3];
      statistic[0] = statisticDay;
      statistic[1] = statisticMonth;
      statistic[2] = statisticYear;
      tag = new String[]{"日", "月", "年"};
      alert = new double[]{
          getAlert(minDayHot, maxDayHot, alertValue),
          getAlert(minMonthHot, maxMonthHot, alertValue),
          getAlert(minYearHot, maxYearHot, alertValue)
      };
    }
    else if (getDistanceMonth.size() < monthShow + 1) {
      statistic = new Map[1];
      statistic[0] = statisticDay;
      tag = new String[]{"日"};
      alert = new double[]{
          getAlert(minDayHot, maxDayHot, alertValue),
      };
    }
    else {
      statistic = new Map[2];
      statistic[0] = statisticDay;
      statistic[1] = statisticMonth;
      tag = new String[]{"日", "月"};
      alert = new double[]{
          getAlert(minDayHot, maxDayHot, alertValue),
          getAlert(minMonthHot, maxMonthHot, alertValue)
      };
    }
    Map<String, Object> typeStatInfo = new HashMap<>();
    typeStatInfo.put("statistic", statistic);
    typeStatInfo.put("tag", tag);
    typeStatInfo.put("alerts", alert);
    LinkedHashMap<String, Object> map = new LinkedHashMap<>();
    long end = System.currentTimeMillis();
    map.put("typeStatInfo", typeStatInfo);
    Map<String, Object> countStatInfo = new HashMap<>();
    double[] countAlert;
    if (axisDay.size() > 365 * 3) {
      countStatInfo.put("statistic", new List[]{
          countDayList, monthCountList, yearCountList
      });
      countAlert = new double[]{
          getAlert(dayMinCount, dayMaxCount, alertValue),
          getAlert(monthMinCount, monthMaxCount, alertValue),
          getAlert(yearMinCount, yearMaxCount, alertValue)
      };


    } else if (getDistanceMonth.size() < monthShow + 1) {
      countStatInfo.put("statistic", new List[]{countDayList});
      countAlert = new double[]{
          getAlert(dayMinCount, dayMaxCount, alertValue)
      };
    } else {
      countStatInfo.put("statistic", new List[]{
          countDayList, monthCountList
      });
      countAlert = new double[]{
          getAlert(dayMinCount, dayMaxCount, alertValue),
          getAlert(monthMinCount, monthMaxCount, alertValue)
      };
    }

    countStatInfo.put("alerts", countAlert);
    countStatInfo.put("tag", tag);
    map.put("countStatInfo", countStatInfo);
    Map<String, Object> hotStatInfo = new HashMap<>();
    if (axisDay.size() > 365 * 3) {
      hotStatInfo.put("statistic", new List[]{
          hotDayList, monthHotList, yearHotList
      });
      hotStatInfo.put("alerts", new double[]{
          getAlert(minDayHot, maxDayHot, alertValue),
          getAlert(minMonthHot, maxMonthHot, alertValue),
          getAlert(minYearHot, maxYearHot, alertValue)

      });
    } else if (getDistanceMonth.size() < monthShow + 1) {
      hotStatInfo.put("statistic", new List[]{
          hotDayList
      });
      hotStatInfo.put("alerts", new double[]{
          getAlert(minDayHot, maxDayHot, alertValue)
      });
    } else {
      hotStatInfo.put("statistic", new List[]{
          hotDayList, monthHotList
      });
      hotStatInfo.put("alerts", new double[]{
          getAlert(minDayHot, maxDayHot, alertValue),
          getAlert(minMonthHot, maxMonthHot, alertValue)
      });
    }
    hotStatInfo.put("tag", tag);
    map.put("hotStatInfo", hotStatInfo);
    JSONObject simNews = new JSONObject();
//    getSimNews();

    JSONObject sim = new JSONObject();
    getSimNews(responseEntity, sim, queryParam.getCurrentPage(), true, detailSimParam.getRecommendType());

    JSONObject data = new JSONObject();
    data.put("axis", map);
    data.put("simNews", sim);
    log.info("{} recommend total cost:{}s",detailSimParam.getRecommendType(), timeUtil.getCost());
    return Message.builder().code(200).message("success").data(data).build();  }

  private Map<String, HotChartValue> wrapCountHotMap(List<String> dateList, Object initialValue) {
    Map<String, HotChartValue> map = new LinkedHashMap<>();
    for (String date: dateList) {
      map.put(date, new HotChartValue(date, initialValue, null));
    }
    return map;
  }

  private double getAlert(int min, int max, double alert){
    return min + (max - min) * alert;
  }

  private double getAlert(float min, float max, double alert){
    return min + (max - min) * alert;
  }


  private float[] wrapHot(List<Float> hot) {
    Float[] hotArray = hot.toArray(new Float[0]);
    Arrays.sort(hotArray);
    float minHot = hotArray[0];
    float maxHot = hotArray[hotArray.length - 1];
    return new float[]{minHot, maxHot};
  }

  private int getColorCast(Integer[] array, int key) {
    if (array.length == 0) {
      return 1;
    }
    int min = array[0];
    int max = array[array.length - 1];
    if (max - min == 0) {
      return 1;
    }
    int a = (int)(((double)(key - min) * 4) / (max - min) + 1);
    if (a == 5)
      a = 4;
    return a;
  }

  private int getColorCast(Float[] array, float key) {
    if (array.length == 0) {
      return 1;
    }
    float min = array[0];
    float max = array[array.length - 1];
    if (max - min == 0) {
      return 1;
    }
    int a = (int)(((double)(key - min) * 4) / (max - min) + 1);
    if (a == 5)
      a = 4;
    return a;
  }


  /**
   * 将返回的相似新闻结果包裹到link里
   * @param link, 最终返回的link
   * @param currentPage, 当前页数
   */
  private void getSimNews(ResponseEntity<JSONObject> responseEntity, JSONObject link, int currentPage, boolean zh, String queryType) {
    JSONArray jsonArray = responseEntity.getBody().getJSONObject("hits").getJSONArray("hits");
    if (jsonArray == null || jsonArray.isEmpty()) {
      jsonArray = new JSONArray();
    }
    List<News> hits = new ArrayList<>();
    link.put("currentPage", currentPage);
    Map<String, Map<String, List<String>>> map2Highlight = new HashMap<>();
    Map<String, Double> id2Score = new HashMap<>();
    for (Object o: jsonArray) {
      JSONObject temp = (JSONObject) o;
      JSONObject source = temp.getJSONObject("_source");
      News news = new News();
      news.setNewsWebsiteType(source.get("news_website_type"));
      news.setNewsWebsite(source.getString("news_website"));
      news.setNewsAuthor(source.get("news_author"));
      news.setNewsPublicTime((String)source.get("news_publictime"));
      news.setNewsKeywords(source.get("news_keywords"));
      news.setId((String)source.get("id"));
      news.setNewsPublicDate((String)source.get("news_publicdate"));
      news.setNewsTitleZh(source.get("news_title_zh"));
      news.setNewsContentZh(source.get("news_content_zh"));

      id2Score.put(news.getId(), (Double)temp.getOrDefault("_score", 0.0));
      if (!queryType.equals("mlt") &&  temp.getJSONObject("highlight") != null)  {
        JSONObject highlight = temp.getJSONObject("highlight");
        HashMap<String, List<String>> tempMap = new HashMap<>();
        for (Object s: highlight.keySet()) {
          Object value = highlight.get(s);
          tempMap.put((String)s, (List<String>)value);
        }
        map2Highlight.put(news.getId(), tempMap);
      }
      hits.add(news);
    }
    List<JSONObject> newsList = new ArrayList<>();

    for (News hit: hits) {
      JSONObject newsLink = new JSONObject();

      ResponseUtil.putCommon(newsLink, hit);
      newsLink.put("news_publictime_date", hit.getNewsPublicDate());

      if (!queryType.equals("mlt")) {
        if (!map2Highlight.containsKey(hit.getId())) {
          newsLink.put("news_content", hit.getNewsContentZh());
          newsLink.put("news_title", hit.getNewsTitleZh());
        }
        else
          ResponseUtil.putTitleContent(map2Highlight.get(hit.getId()), newsLink, hit, zh);
        newsLink.put("news_sourcesite", hit.getNewsWebsite());
      }
      else {
        newsLink.put("news_website_type", hit.getNewsWebsiteType());
        newsLink.put("news_content", hit.getNewsContentZh());
        newsLink.put("news_title", hit.getNewsTitleZh());
        newsLink.put("score", id2Score.get(hit.getId()));
      }
      newsList.add(newsLink);
    }
    link.put("news", newsList);
    Integer num = (responseEntity.getBody().getJSONObject("hits").getJSONObject("total").getInteger("value"));
    link.put("totalResults", (
        num
    ));
  }



  /**
   * 将DetailSimParam 包装成 QueryParam
   * @param baseParam, 即原始的DetailSimParam
   * @param detailPageSize, 相似新闻展示的页数，默认是10
   * @return 包装好的QueryParam
   */
  private QueryParam wrapQueryParam(BaseParam baseParam, Integer detailPageSize) {
    DetailSimParam detailSimParam = (DetailSimParam) baseParam;

    QueryParam queryParam = new QueryParam();
    queryParam.setRecommendType(detailSimParam.getRecommendType());
    String searchingKeywords = getSearchingKeywordsEntities(baseParam, queryParam);
    queryParam.setStartDate(detailSimParam.getStartDate());
    queryParam.setEndDate(detailSimParam.getEndDate());

    queryParam.setCurrentPage(detailSimParam.getCurrentPage());
    if (detailSimParam.getPageSize() == null) {
      queryParam.setPageSize(10);
    } else {
      queryParam.setPageSize(detailSimParam.getPageSize());
    }
    queryParam.setQueryStr(searchingKeywords);

    queryParam.set_id(searchingKeywords);
    queryParam.setSortType("sim");
    queryParam.setZh(true);
    queryParam.setSearchType("false");
    queryParam.setStartDate(detailSimParam.getStartDate());
    queryParam.setEndDate(detailSimParam.getEndDate());
    queryParam.setWebSites(detailSimParam.getWebSites());
    queryParam.setWebSiteType(detailSimParam.getWebSiteType());
    queryParam.setLikeDegree((int)(detailSimParam.getDegree() * 100) + "%");
//    if (Strings.isBlank(queryParam.getWebSiteType())) {
//      queryParam.setWebSiteType("GOV,HMT,WEBSITE");
//    }
    if (Strings.isBlank(queryParam.getStartDate()) || Strings.isBlank(queryParam.getEndDate())) {
      Pair<String, String> aYearStartEnd = RenderDateUtil.getRecent1Year();
      queryParam.setStartDate(aYearStartEnd.getKey());
      queryParam.setEndDate(aYearStartEnd.getValue());
    }
    QueryParamCheckUtil.checkParam(queryParam, detailPageSize);
    log.info("\n\nnow startDate: {}, endDate: {}\n", queryParam.getStartDate(), queryParam.getEndDate());
    return queryParam;
  }



  /**
   * 转换相似性指标为查询字符串
   * @param baseParam, 即原始的DetailSimParam
   * @return 转换好的字符串
   */
  private String getSearchingKeywordsEntities(BaseParam baseParam, QueryParam queryParam) {
    DetailSimParam detailSimParam = (DetailSimParam) baseParam;
    List<String> searchingKeywords = new ArrayList<>();
    if (detailSimParam.getRecommendType().equals("mlt")) {
      SearchRequest searchRequest = SearchRequest.of(x -> x.index(buildRequest.partitionIndex(config.getIndex(), queryParam.getStartDate(), queryParam.getEndDate())).query(q -> q.term(t -> t.field("id").value(detailSimParam.getId()))));

      try {
        SearchResponse<News> search = elasticsearchClient.search(searchRequest, News.class);
        Hit<News> hit = search.hits().hits().get(0);
        queryParam.setIndex(hit.index());
        return hit.id();
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
    }
    if (Strings.isNotBlank(detailSimParam.getKeywords())) {
      searchingKeywords.addAll(Lists.newArrayList(
          detailSimParam.getKeywordsList().stream().map(FieldValue::_toJsonString).iterator()));
    } else {
      String id = ((DetailSimParam) baseParam).getId();
      DetailParam detailParam = DetailParam.builder().id(id).build();
      Message detail = detailService.exec(detailParam);
      JSONObject data = (JSONObject) detail.getData();
      if (detailSimParam.getRecommendType().equals("keywords")) {
        searchingKeywords.addAll(EsTypeTransUtil.keyword2List(data.get("keywords")));
        queryParam.getKeywordsWithScore().addAll((List<Map<String, Object>>) data.get("keywords"));
      } else {
        searchingKeywords.addAll(EsTypeTransUtil.entity2List(data.get("news_entities")));
      }
    }
    if (!searchingKeywords.isEmpty()) {
      searchingKeywords = searchingKeywords.subList(0, Math.min(config.getRecommendKeywordsNum(), searchingKeywords.size()));
    }

//    searchingKeywords.replaceAll(s1 -> "(\"" + s1 + "\")");
    return String.join(" ", searchingKeywords);
  }
}
